﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data.Services.Client;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using BuildARoute.BARDataService;
using Microsoft.Maps.MapControl;
using BuildARoute.Bing.Route;
using System.Windows.Media;

namespace BuildARoute.Views
{
    public partial class CreateTourWindow : ChildWindow, INotifyPropertyChanged
    {

        #region fields and props
        private CollectionViewSource poiCollectionViewSource;
        public TourGuideSolutionDataEntities Context { get; set; }
        private bool isNew = false;
        public bool IsNew
        {
            get
            {
                return isNew;
            }
            set
            {
                isNew = value;
            }
        }
        private Models.MapInfo _mapInfo = new Models.MapInfo();
        public Models.MapInfo MapInfo
        {
            get
            {
                return _mapInfo;
            }
        }


        private Tour _currentTour = new Tour();
        public Tour CurrentTour
        {
            get
            {
                return _currentTour;
            }
            set
            {
                if (_currentTour == value) return;
                _currentTour = value;
                RaisePropertyChanged("CurrentTour");
            }
        }

        private bool _isBusyIndicatorOn = false;
        public bool IsBusyIndicatorOn
        {
            get
            {
                return _isBusyIndicatorOn;
            }
            set
            {
                if (value == _isBusyIndicatorOn) return;
                _isBusyIndicatorOn = value;
                RaisePropertyChanged("IsBusyIndicatorOn");
            }
        }

        #endregion

        #region ctor
        public CreateTourWindow()
        {
            InitializeComponent();

            this.Loaded += new RoutedEventHandler(CreateTourWindow_Loaded);
        }

        #endregion

        #region methods and event handlers
        void CreateTourWindow_Loaded(object sender, RoutedEventArgs e)
        {
            Context.MergeOption = MergeOption.PreserveChanges;

            IsBusyIndicatorOn = true;

            poiCollectionViewSource = this.Resources["poiCollectionViewSource"] as CollectionViewSource;

            if (!IsNew)
            {
                //Load associated POIs
                CurrentTour.POIs.LoadCompleted += new EventHandler<LoadCompletedEventArgs>(POIs_LoadCompleted);
                CurrentTour.POIs.LoadAsync();

            }
            else 
            {
                IsBusyIndicatorOn = false;
            }


            
            editDataForm.CurrentItem = CurrentTour;
            editDataForm.BeginEdit();
        }


        void POIs_LoadCompleted(object sender, LoadCompletedEventArgs e)
        {
            var entities = sender as DataServiceCollection<POI>;
            if (e.Error == null)
            {
                if (entities.Continuation != null)
                {
                    //Continue loading
                    entities.LoadNextPartialSetAsync();
                }
                else
                {
                    //Bind
                    poiCollectionViewSource.Source = entities;
                    drawRoute();
                    IsBusyIndicatorOn = false;
                }
            }
            else
            {
                MessageBox.Show(string.Format("Problem loading entities: {0}", entities.ToString()));
            }
        }

        private void drawRoute()
        {
            if (CurrentTour.POIs.Count > 1)
            {


                RouteServiceClient client = new RouteServiceClient("BasicHttpBinding_IRouteService");
                client.CalculateRouteCompleted += new System.EventHandler<CalculateRouteCompletedEventArgs>(client_CalculateRouteCompleted);

                //Calculate route request
                RouteRequest request = new RouteRequest();
                request.Credentials = new Credentials();
                request.Credentials.ApplicationId = MapInfo.ID;

                ObservableCollection<Waypoint> waypoints = new ObservableCollection<Waypoint>();
                foreach (var item in CurrentTour.POIs)
                {
                    var wp = new Waypoint();
                    wp.Location = new Location(item.Latitude, item.Longitude, 0, AltitudeReference.Ground);
                    waypoints.Add(wp);

                }
                request.Waypoints = waypoints;

                RouteOptions options = new RouteOptions();
                options.RoutePathType = RoutePathType.Points;
                request.Options = options;

                client.CalculateRouteAsync(request);

                setMapView();
            }
        }

        void client_CalculateRouteCompleted(object sender, CalculateRouteCompletedEventArgs e)
        {
            //// If the route calculate was a success and contains a route, then draw the route on the map.
            if ((e.Result.ResponseSummary.StatusCode == ResponseStatusCode.Success) & (e.Result.Result.Legs.Count != 0))
            {
                // Set properties of the route line you want to draw.

                SolidColorBrush routeBrush = new SolidColorBrush(Colors.Blue);
                MapPolyline routeLine = new MapPolyline();
                routeLine.Locations = new LocationCollection();
                routeLine.Stroke = routeBrush;
                routeLine.Opacity = 0.65;
                routeLine.StrokeThickness = 3.0;

                // Retrieve the route points that define the shape of the route.
                foreach (Location p in e.Result.Result.RoutePath.Points)
                {
                    routeLine.Locations.Add(new Location(p.Latitude, p.Longitude, 0, AltitudeReference.Ground));
                }

                //Clean routeLayer
                routeLayer.Children.Clear();

                // Add the route line to the new layer.
                routeLayer.Visibility = Visibility.Visible;
                routeLayer.Children.Add(routeLine);
            }
        }

        private void setMapView()
        {
            if (CurrentTour.POIs.Count > 1)
            {
                var points = new List<Location>();
                foreach (var item in CurrentTour.POIs)
                {
                    points.Add(new Location(item.Latitude, item.Longitude, 0, AltitudeReference.Ground));
                }
                LocationRect locrect = new LocationRect(points);
                mapControl.SetView(locrect);
            }
        }

        private void trySaveChanges()
        {
            try
            {
                IsBusyIndicatorOn = true;
                Context.BeginSaveChanges(SaveChangesOptions.Batch, OnChangesSaved, Context);
                drawRoute();
            }
            catch (Exception ex)
            {
                MessageBox.Show(string.Format("The changes could not be saved to the data service.\n"
                    + "The following error occurred: {0}", ex.Message));
            }
        }

        private void OnChangesSaved(IAsyncResult result)
        {

            bool errorOccured = false;

            // Use the Dispatcher to ensure that the 
            // asynchronous call returns in the correct thread.
            Dispatcher.BeginInvoke(() =>
            {
                Context = result.AsyncState as TourGuideSolutionDataEntities;

                // Complete the save changes operation and display the response.
                DataServiceResponse response = Context.EndSaveChanges(result);
                IsBusyIndicatorOn = false;
                foreach (ChangeOperationResponse changeResponse in response)
                {
                    if (changeResponse.Error != null) errorOccured = true;
                }
                if (!errorOccured)
                {
                    //TODO: let the user know, perhaps through status bar that stuff was written to the database succesfully
                }
                else
                {
                    MessageBox.Show("An error occured. One or more changes could not be saved.");
                }

            }
            );
        }

        private void languageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            CurrentTour.Language = ((sender as ComboBox).SelectedItem as Language).LanguageID;
        }

        private bool initializing = false;
        private void poiDataForm_AddingNewItem(object sender, DataFormAddingNewItemEventArgs e)
        {
            initializing = true;
        }

        private void poiDataForm_ContentLoaded(object sender, DataFormContentLoadEventArgs e)
        {
            if (initializing)
            {
                var poi = poiDataForm.CurrentItem as POI;

                poi.Language = CurrentTour.Language;
                poi.POIID = Guid.NewGuid();
                poi.Provider = WebContext.Current.User.GetIdentity() as string;
                poi.Latitude = 45.423094;
                poi.Longitude = -75.701551;
                poi.DateCreated = DateTime.Now;
                poi.LastEdit = DateTime.Now;
                poi.Duration = 10;

                initializing = false;
            }
        }


        void mapControl_MouseDoubleClick(object sender, MapMouseEventArgs e)
        {
            var poi = new POI();
            poi.Title = "";
            poi.Language = CurrentTour.Language;
            poi.POIID = Guid.NewGuid();
            poi.Provider = WebContext.Current.User.GetIdentity() as string;
            poi.DateCreated = DateTime.Now;
            poi.LastEdit = DateTime.Now;
            poi.Duration = 10;

            Location loc;
            mapControl.TryViewportPointToLocation(e.ViewportPoint, out loc);

            poi.Latitude = loc.Latitude;
            poi.Longitude = loc.Longitude;

            CurrentTour.POIs.Add(poi);
            poiCollectionViewSource.Source = CurrentTour.POIs;
            drawRoute();

            poiDataForm.CurrentItem = poi;
            poiDataForm.BeginEdit();
        }

        private void poiDataForm_EditEnded(object sender, DataFormEditEndedEventArgs e)
        {
            var poi = poiDataForm.CurrentItem as POI;
            poi.LastEdit = DateTime.Now;
            trySaveChanges();
            drawRoute();
        }


        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;
        

        protected void RaisePropertyChanged(string propName)
        {
            var propChanged = PropertyChanged;
            if (propChanged != null)
            {
                propChanged(this, new PropertyChangedEventArgs(propName));
            }
        }
        #endregion

        private void closeButton_Click(object sender, RoutedEventArgs e)
        {
            CurrentTour.LastEdit = DateTime.Now;

            poiDataForm.CommitEdit();
            editDataForm.CommitEdit();

            trySaveChanges();
            this.DialogResult = true;
        }

    }
}